﻿#region License
/***********************************************
 *  WCFReactiveX v.1
 *  Written by James Levingston
 *  
 *  To get latest version or to see latest info:
 *  http://wcfreactivex.codeplex.com/
 *  
 *  The MIT License (MIT)
 *  http://en.wikipedia.org/wiki/MIT_License
 *  Copyright (C) <2011> by <James Levingston>
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.

 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 ***/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.ServiceModel;
using System.Configuration;


// This software uses the most powerful mapping tool in the solar system
// http://automapper.codeplex.com/
using AutoMapper; 

namespace WCFReactiveX
{
    public static class Extensions
    {
        /// <summary>
        /// Closes the connection; if closing fails, abort is called and the exception thrown
        /// </summary>
        /// <param name="obj"></param>
        public static void CloseConnection(this ICommunicationObject obj)
        {
            if (obj == null)
                return;

            if (obj.State == CommunicationState.Faulted || obj.State == CommunicationState.Closed)
                obj.Abort();
            else
                try { obj.Close(); }
                catch
                {
                    obj.Abort();
                    throw;
                }
        }
        /// <summary>
        /// Gets a config value from the app.config file
        /// </summary>
        /// <param name="obj">any object</param>
        /// <param name="keyName">confing entry key name</param>
        /// <returns>value in config</returns>
        public static string GetConfigEntry(this object obj, string keyName)
        {
            if (keyName == null)
                throw new ConfigurationErrorsException("Error: Cannot get config entry, keyname is null");

            try
            {
                var item = ConfigurationManager.AppSettings[keyName];
                if (!string.IsNullOrEmpty(item))
                    item.Trim();
                return item;
            }
            catch (Exception ex)
            {
                throw new ConfigurationErrorsException("Error: Please check the application .config file and verify key '" + keyName + "' exists.", ex);
            }
        }
        /// <summary>
        /// Maps an object to a delimited string
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="delimiter"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        public static string ToDelimitedString<T>(this T obj, char delimiter, Func<T, System.Reflection.PropertyInfo, string> func)
        {
            if (obj == null || func == null)
                return null;

            var builder = new System.Text.StringBuilder();
            var props = obj.GetType().GetProperties();
            for (int p = 0; p < props.Length; p++)
                builder.Append(func(obj, props[p]) + delimiter.ToString());

            //Remove the last character, the last delimiter
            if (builder.Length > 0)
                return builder.ToString().Remove(builder.ToString().Length - 1);

            return null;
        }
        /// <summary>
        /// Maps an object to a pipe delimited string
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ToPipeDelimitedString(this object obj)
        {
            if (obj == null)
                return null;

            return obj.ToDelimitedString<object>('|',
                (object o, System.Reflection.PropertyInfo p) => { return (string.Format("{0}.{1}={2}", p.ReflectedType.Name, p.Name, Convert.ToString(p.GetValue(o, null)))); });
        }
        /// <summary>
        /// Converts an enum to another by parsing the string value of the enum and casting it to T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="val"></param>
        /// <returns></returns>
        public static T ConvertEnumToEnum<T>(this Enum val) where T : struct
        {
            return (T)Enum.Parse(typeof(T), val.ToString());
        }
        /// <summary>
        /// Map each item in a collection
        /// </summary>
        /// <typeparam name="SourceT"></typeparam>
        /// <typeparam name="DestinationT"></typeparam>
        /// <param name="items"></param>
        /// <param name="mapFunction"></param>
        /// <returns></returns>
        public static List<DestinationT> ToListMap<SourceT, DestinationT>(this IList<SourceT> items, Func<SourceT, DestinationT> mapFunction)
        {
            if (items == null || mapFunction == null)
                return new List<DestinationT>();

            List<DestinationT> coll = new List<DestinationT>();
            for (int i = 0; i < items.Count(); i++)
            {
                DestinationT val = mapFunction(items.ElementAt<SourceT>(i));
                if (val != null)
                    coll.Add(val);
            }
            return coll;
        }
        /// <summary>
        /// Map obect to type T
        /// </summary>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static TDestination MapToObject<TDestination>(this object source)
        {
            if (source == null)
                return default(TDestination);

            Mapper.CreateMap<object, TDestination>();
            return Mapper.Map<object, TDestination>(source);
        }
        /// <summary>
        /// Map to type TDestination passing in object to map to.
        /// </summary>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        /// <returns></returns>
        public static TDestination ConstructMapToObject<TDestination>(this object source, TDestination destination) where TDestination : new()
        {
            if (source == null)
                return default(TDestination);

            Mapper.CreateMap<object, TDestination>().ConstructUsing(x => destination);
            return Mapper.Map<object, TDestination>(source);
        }
        /// <summary>
        /// Map type TSource to type TDestination passing in TSource
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="obj"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        public static TDestination MapObject<TSource, TDestination>(this object obj, TSource source)
        {
            Mapper.CreateMap<TSource, TDestination>();
            return Mapper.Map<TSource, TDestination>(source);
        }
        /// <summary>
        /// Map type TSource to type TDestination passing in an expression list of functions. 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="obj"></param>
        /// <param name="source"></param>
        /// <param name="ignoreList"></param>
        /// <returns></returns>
        public static TDestination MapObject<TSource, TDestination>(this object obj, TSource source, List<Expression<Func<TDestination, object>>> ignoreList)
        {
            ignoreList.ForEach(funcExpress => Mapper.CreateMap<TSource, TDestination>().ForMember(funcExpress, opt => opt.Ignore()));
            return Mapper.Map<TSource, TDestination>(source);
        }
        /// <summary>
        /// Map type TSource to type TDestination
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="obj"></param>
        /// <param name="destination"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        public static TDestination ConstructMapObject<TSource, TDestination>(this object obj, TDestination destination, TSource source) where TDestination : new()
        {
            Mapper.CreateMap<TSource, TDestination>().ConstructUsing(x => destination);
            return Mapper.Map<TSource, TDestination>(source);
        }
        /// <summary>
        /// Map type TSource to type TDestination, passing the source, destination and an ignorelist of properties to ignore when mapping
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="obj"></param>
        /// <param name="destination"></param>
        /// <param name="source"></param>
        /// <param name="ignoreList"></param>
        /// <returns></returns>
        public static TDestination ConstructMapObject<TSource, TDestination>(
            this object obj,
            TDestination destination,
            TSource source,
            List<Expression<Func<TDestination, object>>> ignoreList)
        {
            Mapper.CreateMap<TSource, TDestination>().ConstructUsing(x => destination);
            ignoreList.ForEach(funcExpress => Mapper.CreateMap<TSource, TDestination>().ForMember(funcExpress, opt => opt.Ignore()));
            return Mapper.Map<TSource, TDestination>(source);
        }
    }
}
